#include "dlslib.h"
#include "syncommon.h"

#include <math.h>

namespace Audio
{
	struct FormatChunk {
		short          wFormatTag;
		unsigned short wChannels;
		unsigned long  dwSamplesPerSec;
		unsigned long  dwAvgBytesPerSec;
		unsigned short wBlockAlign;
		unsigned short wBitsPerSample;

		// Note: there may be additional fields here, depending on wFormatTag.
	};

	bool DLS::Open(const TCHAR *filename) {
		if (!file.Open(filename))
			return false;
		if (file.Descend('RIFF', 'DLS ')) {
			if (file.Descend('LIST', 'lins')) {
				noInstr = 0;
				while (file.Descend('LIST', 'ins ')) {
					if (file.Descend('insh')) {
						instruments[noInstr].nRegions = file.Read();
						instruments[noInstr].bank = file.Read();
						instruments[noInstr].number = file.Read(); //instrument number
						file.Ascend();
					}
					/*
					RegionArticulation *globalArt = 0;

					if (file.Descend('LIST', 'lart'))
					{
						globalArt = (RegionArticulation*)file.GetChunkPtr();
						file.Ascend();
					}*/
					file.Restart();

					if (file.Descend('LIST', 'lrgn')) {
						int i = 0;
						while (file.Descend('LIST', 'rgn ')) {
							Region &r = instruments[noInstr].regions[i];
							if (file.Descend('rgnh')) {
								r.header = (const RegionHeader *)file.GetChunkPtr();
								file.Ascend();
							}
							file.Restart();
							if (file.Descend('wlnk')) {
								file.Read(); //yuck
								file.Read(); //channel
								r.waveIndex = file.Read();
								file.Ascend();
							}
							file.Restart();
							if (file.Descend('wsmp')) {
								int curpos = file.Tell();
								int cbSize = file.Read();
								r.misc = (const RegionMisc *)file.GetChunkPtr();
								file.Seek(curpos + cbSize - 4);
								int nLoops = file.Read();
								if (nLoops)
									r.loop = (const RegionLoop *)file.GetChunkPtr();
								else
									r.loop = 0;
								file.Ascend();
							}
							file.Restart();
							if (file.Descend('LIST','lart')) {
								r.art = (const RegionArticulation *)file.GetChunkPtr();
								file.Ascend();
							} else {
//								r.art = globalArt;
							}
							file.Ascend();
							i++;
						}
						file.Ascend();
					}
					file.Ascend();
					noInstr++;
				}
				file.Ascend();
			}

			if (file.Descend('LIST', 'wvpl')) {
				noSamples = 0;
				while (file.Descend('LIST', 'wave')) {
					if (file.Descend('fmt ')) {
						const FormatChunk *fmt = (const FormatChunk *)file.GetChunkPtr();
						samples[noSamples].sampleRate = fmt->dwSamplesPerSec;
						file.Ascend();
					}
					if (file.Descend('data')) {
						samples[noSamples].pointer = (const short *)file.GetChunkPtr();
						samples[noSamples].length = file.GetChunkSize() / 2;
						file.Ascend();
					}
					noSamples++;
					file.Ascend();
				}
				file.Ascend();
			}
			file.Ascend();
		}
		return true;
	}

	//GREAT combo: 128, 100
	int DLS::GetRegionAndNoteOffset(int instrument, float fnote, float *noteOffset, float *sr) {
		const Instrument &ins = instruments[instrument];
		int note = (int)(fnote + 0.5f);
		int region = -1;
		for (int i = 0; i < ins.nRegions; i++) {
			if (note >= ins.regions[i].header->rangeKeyLow &&
				note <= ins.regions[i].header->rangeKeyHigh) {
				region = i;
				break;
			}
		}
		if (region != -1) {
			const Region &r = ins.regions[region];
			*noteOffset = -(float)r.misc->unityNote + (float)(r.misc->fineTune) * (1.0f/100.f);
			*sr = ((float)samples[r.waveIndex].sampleRate / INTERNAL_SAMPLE_RATE);
			return region;
		}
		else
			return -1;
	}

	void DLS::Close() {
		file.Close();
	}
}